<!--- 
	UserService.cfc
	Provides access to bean createion and validation, application specific business logic 
	and save/load operations contained in DAOs and gateways.
	
	Author: Felix.T
 --->
<cfcomponent output="false" cache="false">
	<!------------------------------------------- CONSTRUCTOR ------------------------------------------->
	<cffunction name="init" access="public" returntype="users" output="false">
		<cfscript>
			variables.oUserGateway = Application.oUserGateway;
			variables.oUserGateway.setRequestContent( rc );			
			return this;		
		</cfscript>
	</cffunction>
	<!------------------------------------------- PUBLIC ------------------------------------------->

	<cffunction name="validateUser" access="public" returntype="struct" output="true">
		<cfargument name="Event" type="coldbox.system.beans.requestContext" required="true">
		<cfscript>
			var rtnStruct = structnew();
			var thePassword = "";
			var rc = event.getCollection();
			rtnStruct.validated = false;
			rtnStruct.userID = 0;
			rtnStruct.username = "";
			stArguments = StructNew();
			stArguments.username = rc.username;
			stArguments.password = rc.password;
			Application.oUserGateway.setTransfer( rc.Transfer );//TODO: redundant
			userBean = Application.oUserGateway.getUserByCredentials(stArguments);
 			if (userBean.getUser_ID() gt 0) {
 				rtnStruct.validated = true;
 				rtnStruct.userID = userBean.getUser_ID();
 				rtnStruct.username = userBean.getUserName();
 			}
			return rtnStruct;
		</cfscript>
	</cffunction>

	<cffunction name="getUserAttempts" access="public" returntype="query" output="false">
		<cfargument name="Event" type="coldbox.system.beans.requestContext" required="true">
		<cfargument name="user_id" type="string" required="true">
		<cfscript>
			var rc = event.getCollection();
			Application.oUserGateway.setTransfer( rc.Transfer );//TODO: redundant
			return Application.oUserGateway.getUserAttempts( Val(Arguments.user_id) );			
		</cfscript>
	</cffunction>

	<cffunction name="createAttempt" access="public" returntype="struct" output="false">
		<cfargument name="Event" type="coldbox.system.beans.requestContext" required="true">
		<cfargument name="user_id" type="string" required="true">
		<cfset var rc = event.getCollection()>
		<cfset error = "">
		<cfset qQuestions = rc.Transfer.list("exam.question")>
		<!--- we need to randomize pick questions from the query, do it here --->
		<!---
		<cfset Application.QuestionsPerAttempt = 10>
		<cfset lQuestionIDs = "">
		<cfloop from="1" to="#Application.QuestionsPerAttempt#">
			<cfset randomID = RandRange(1,qQuestion.recordCount)>
			<cfloop condition="ListFind(lQuestionIDs,randomID)">
				<cfset randomID = RandRange(1,qQuestion.recordCount)>>
			</cfloop>
			<!--- check if ID exists --->
			<cfquery name="qCheckID" dbtype="query">
				SELECT * FROM qQuestions WHERE question_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(RandomID)#">
			</cfquery>
			<cfloop query condition="qCheckID.recordCount eq 0">
				<cfset randomID = RandRange(1,qQuestion.recordCount)>
				<cfquery name="qCheckID" dbtype="query">
					SELECT * FROM qQuestions WHERE question_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(RandomID)#">
				</cfquery>
			</cfloop>

			<cfset lQuestionIDs = lQuestionIDs & "#randomID#,">			
		</cfloop>	
		--->	
		<cfquery name="qRandomizedQuestions" dbtype="query">
			SELECT * FROM qQuestions 
			<!--- If randomize thingy is working
			WHERE question_id IN 
			(<cfqueryparam cfsqltype="cf_sql_integer" list="true" value="#lQuestionIDs#">)	
			--->	
		</cfquery>
		<cfset lQuestionIDs = ValueList(qQuestions.question_id)>
		
		<cftry>
			<cfscript>
				oAttempt = rc.Transfer.new("exam.attempt");
				oUser = rc.Transfer.get("user.user", Arguments.user_id );
				oAttempt.setIs_Finish(0);
				rc.Transfer.save( oAttempt ); //TODO: cannot save attempt
				new_attempt_id = oAttempt.getAttempt_ID();
			</cfscript>
			<cfloop list="#lQuestionIDs#" index="listIndex">
				<cfscript>
					/* Many to many relationship */
					oAttemptQuestion= rc.Transfer.new("exam.attempt_question");
					oAttemptQuestion.setAttempt_id(new_attempt_id);
					oAttemptQuestion.setQuestion_id(listIndex);
					rc.Transfer.save(oAttemptQuestion);
				</cfscript>
			</cfloop>
			<cfcatch type="any">
				<cfset error = CFCATCH.Detail & " " & CFCATCH.Message>
			</cfcatch>
		</cftry>
		<cfset stReturn = StructNew()>
		<cfset stReturn.error = error>
		<cfreturn stReturn>
	</cffunction>
	
	<!--- need to rewrite this --->
	<cffunction name="getCurrentAttempt" access="public" returntype="numeric" output="false" cache="false">
		<cfargument name="Event" type="coldbox.system.beans.requestContext" required="true">
		<cfargument name="user_id" type="string" required="true">
		
		<cfscript>
			var rc = event.getCollection();
			Application.oUserGateway.setTransfer( rc.Transfer );//TODO: redundant
			qCurrentAttempt = Application.oUserGateway.getCurrentAttempt( Val(Arguments.user_id) );			
		</cfscript>

		<cfif Val(qCurrentAttempt.attempt_id) eq 0>
			<cfthrow detail="Error" type="Application" message="query error">
		<cfelse>
			<cfreturn Val(qCurrentAttempt.attempt_id)>
		</cfif>
	</cffunction>
	
	<cffunction name="finishAttempt" access="public" returntype="any" output="false" cache="false">
		<cfargument name="attempt_id" type="string" required="true">
		<!--- what i need to do is the check all the question_answers for attempt and see whether they are correct --->
		<cfset qUserAttemptQuestions = getAttemptQuestions(Val(Arguments.attempt_id))>
		<cfloop query="qUserAttemptQuestions">
			<cfset qQuestionAnswers = getQuestionCorrectAnswers(question_id)>
			<cfset lCorrectAnswers = ValueList(qQuestionAnswers.answer_id)>
			<!--- what is the most efficient way to compare lists --->
			<cfset isCorrect = 1>
			<cfloop list="#user_answer_ids#" index="thisAnswer">
				<cfif NOT listFind(lCorrectAnswers,thisAnswer)>
					<cfset isCorrect = 0>
				</cfif>
			</cfloop>
			<cfif ListLen(lCorrectAnswers) neq ListLen(user_answer_ids)>
				<cfset isCorrect = 0>
			</cfif>
			<cfset setAttemptQuestion(attempt_id,question_id,Val(isCorrect))>
		</cfloop>
		<!--- Finally we want to update the final mark --->
	</cffunction>

	<cffunction name="setAttemptQuestion" access="public" returntype="void" output="false">
		<cfargument name="attempt_id" type="string" required="true">
		<cfargument name="question_id" type="string" required="true">
		<cfargument name="is_correct" type="string" required="true">
		<cfscript>
			var rc = event.getCollection();
			Application.oUserGateway.setTransfer( rc.Transfer );//TODO: redundant
			Application.oUserGateway.setAttemptQuestion( Arguments );			
		</cfscript>
	</cffunction>

	<cffunction name="getAttemptQuestions" access="public" returntype="query" output="false">
		<cfargument name="attempt_id" type="string" required="true">
		<cfquery name="qQuestions" datasource="#Application.PDS#" username="#Application.DBUSER#" 
				password="#Application.DBPASS#" >
			SELECT 
				Q.*,AQ.user_answer_ids,AQ.is_correct 
			FROM 
				attempt_question AQ 
				LEFT JOIN question Q ON AQ.question_id = Q.question_id
			WHERE 
				AQ.attempt_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(Arguments.attempt_id)#">
		</cfquery>
		<cfreturn qQuestions>
	</cffunction>

	<cffunction name="getQuestionAnswers" access="public" returntype="query" output="false">
		<cfargument name="question_id" type="string" required="true">
		<cfquery name="qQuestions" datasource="#Application.PDS#" username="#Application.DBUSER#" 
				password="#Application.DBPASS#" >
			SELECT A.* 
			FROM 
				answer A 
				LEFT JOIN question Q ON A.question_id = Q.question_id
			WHERE 
				A.question_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(Arguments.question_id)#">
		</cfquery>
		<cfreturn qQuestions>
	</cffunction>
	
	<cffunction name="getQuestionCorrectAnswers" access="public" returntype="query" output="false">
		<cfargument name="question_id" type="string" required="true">
		<cfquery name="qQuestions" datasource="#Application.PDS#" username="#Application.DBUSER#" 
				password="#Application.DBPASS#" >
			SELECT 
				A.answer_id 
			FROM answer A 
				LEFT JOIN question Q ON A.question_id = Q.question_id
			WHERE 
				A.question_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(Arguments.question_id)#">
				AND A.is_correct = 1
		</cfquery>
		<cfreturn qQuestions>
	</cffunction>	
	
	<cffunction name="deleteUser" access="public" returntype="void" output="false">
		<cfargument name="Event" type="coldbox.system.beans.requestContext">
		<cfargument name="user_id" type="string">
		<cfset var rc = event.getCollection()>
		<cfset rc.Transfer.delete(rc.Transfer.get("user.user",Val(Arguments.user_id)))>
		<!---<cfset getPlugins("messagebox").setMessage("info","User(s) removed successfully")>--->
	</cffunction>

	<cffunction name="getAnswers" access="public" returntype="query" output="false">
		<cfargument name="lQuestionIDs" type="string" required="true">
		<cfquery name="qQuestions" datasource="#Application.PDS#" username="#Application.DBUSER#" 
				password="#Application.DBPASS#" >
			SELECT A.* FROM answer A 
				LEFT JOIN question Q ON A.question_id = Q.question_id
			WHERE A.question_id IN (<cfqueryparam cfsqltype="cf_sql_integer" list="true" value="#Val(Arguments.lQuestionIDs)#">)
		</cfquery>
		<cfreturn qQuestions>
	</cffunction>
	<cffunction name="saveUserAnswer" access="public" returntype="string" output="false">
		<cfargument name="attempt_id" type="string" required="true">
		<cfargument name="question_id" type="string" required="true">
		<cfargument name="answer_ids" type="string" required="true">
		<cfset var error = "">
		<cftry>
			<cfquery name="qQuestions" datasource="#Application.PDS#" username="#Application.DBUSER#" 
					password="#Application.DBPASS#" >
				UPDATE attempt_question
				SET user_answer_ids = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Arguments.answer_ids#">
				WHERE
				attempt_id = <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(Arguments.attempt_id)#">
				AND
				question_id =  <cfqueryparam cfsqltype="cf_sql_integer" value="#Val(Arguments.question_id)#">
			</cfquery>
			<cfcatch type="any"><cfset error = CFCATCH.Detail & " " & CFCATCH.Message></cfcatch>
		</cftry>
		<cfreturn error>
	</cffunction>

	<cffunction name="getUsers" access="public" returntype="query" output="false">
		<cfreturn instance.qUsers>
	</cffunction>

	<cffunction name="getInstance" access="public" returntype="struct" output="false">
		<cfreturn instance>
	</cffunction>

	<!---
	<cffunction name="getUserAttempts" access="public" returntype="query" output="false">
		<cfargument name="user_id" type="string" required="true">
		<cfreturn rc.Transfer.listByProperty("exam.attempt","user_id",Val(Arguments.user_id))>
	</cffunction>
	<cffunction name="getUserAttempts" access="public" returntype="Any" output="false">
		<cfargument name="stArguments" type="struct" required="true">
		<cfreturn rc.Transfer.readByPropertyMap("user.user", stArguments)>
	</cffunction>
	--->
	
	
</cfcomponent>